using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XRpgLibrary
{
    public enum GameCommands
    {
        MoveUp,
        MoveDown,
        MoveLeft,
        MoveRight,
        ScrollUp,
        ScrollDown,
        ScrollLeft,
        ScrollRight,
        Interact,
        ActivateMainMenu,
        ToggleCameraMode,
        ActivateDebugHUD,
        ZoomIn,
        ZoomOut,
        MenuAccept,
        MenuCancel
    }

    public class InputHandler : Microsoft.Xna.Framework.GameComponent
    {
        private static Keys[] keyMap;
        static Keys[] KeyMap
        {
            get
            {
                if (keyMap == null)
                {
                    keyMap = new Keys[Enum.GetValues(typeof(GameCommands)).Length];
                    keyMap[(int)GameCommands.MoveUp] = Keys.W;
                    keyMap[(int)GameCommands.MoveDown] = Keys.S;
                    keyMap[(int)GameCommands.MoveLeft] = Keys.A;
                    keyMap[(int)GameCommands.MoveRight] = Keys.D;
                    keyMap[(int)GameCommands.ScrollUp] = Keys.Up;
                    keyMap[(int)GameCommands.ScrollDown] = Keys.Down;
                    keyMap[(int)GameCommands.ScrollLeft] = Keys.Left;
                    keyMap[(int)GameCommands.ScrollRight] = Keys.Right;
                    keyMap[(int)GameCommands.Interact] = Keys.Space;
                    keyMap[(int)GameCommands.ActivateMainMenu] = Keys.Escape;
                    keyMap[(int)GameCommands.ToggleCameraMode] = Keys.F;
                    keyMap[(int)GameCommands.ActivateDebugHUD] = Keys.NumPad0;
                    keyMap[(int)GameCommands.ZoomIn] = Keys.PageUp;
                    keyMap[(int)GameCommands.ZoomOut] = Keys.PageDown;
                    keyMap[(int)GameCommands.MenuAccept] = Keys.Enter;
                    keyMap[(int)GameCommands.MenuCancel] = Keys.Escape;
                }

                return keyMap;
            }
        }

        #region Keyboard Field Region
        
        static KeyboardState keyboardState;
        static KeyboardState lastKeyboardState;
        
        #endregion
        
        #region Game Pad Field Region
        
        static GamePadState[] gamePadStates;
        static GamePadState[] lastGamePadStates;
        
        #endregion
        
        #region Keyboard Property Region
        
        public static KeyboardState KeyboardState
        {
            get { return keyboardState; }
        }

        public static KeyboardState LastKeyboardState
        {
            get { return lastKeyboardState; }
        }

        #endregion

        #region Game Pad Property Region

        public static GamePadState[] GamePadStates
        {
            get { return gamePadStates; }
        }

        public static GamePadState[] LastGamePadStates
        {
            get { return lastGamePadStates; }
        }

        #endregion

        #region Constructor Region

        public InputHandler(Game game)
            : base(game)
        {
            keyboardState = Keyboard.GetState();
            gamePadStates = new GamePadState[Enum.GetValues(typeof(PlayerIndex)).Length];
            foreach (PlayerIndex index in Enum.GetValues(typeof(PlayerIndex)))
                gamePadStates[(int)index] = GamePad.GetState(index);
        }

        #endregion

        #region XNA methods
        
        public override void Initialize()
        {
            base.Initialize();
        }
        
        public override void Update(GameTime gameTime)
        {
            lastKeyboardState = keyboardState;
            keyboardState = Keyboard.GetState();
            lastGamePadStates = (GamePadState[])gamePadStates.Clone();
            foreach (PlayerIndex index in Enum.GetValues(typeof(PlayerIndex)))
                gamePadStates[(int)index] = GamePad.GetState(index);
            base.Update(gameTime);
        }
        
        #endregion
        
        #region General Method Region
        
        public static void Flush()
        {
            lastKeyboardState = keyboardState;
        }
        
        #endregion
        
        #region Keyboard Region

        public static bool KeyReleased(GameCommands command)
        {
            Keys key = KeyMap[(int)command];
            return keyboardState.IsKeyUp(key) &&
            lastKeyboardState.IsKeyDown(key);
        }

        public static bool KeyPressed(GameCommands command)
        {
            Keys key = KeyMap[(int)command];
            return keyboardState.IsKeyDown(key) &&
            lastKeyboardState.IsKeyUp(key);
        }

        public static bool KeyDown(GameCommands command)
        {
            Keys key = KeyMap[(int)command];
            return keyboardState.IsKeyDown(key);
        }

        public static bool KeyReleased(Keys key)
        {
            return keyboardState.IsKeyUp(key) &&
            lastKeyboardState.IsKeyDown(key);
        }
        
        public static bool KeyPressed(Keys key)
        {
            return keyboardState.IsKeyDown(key) &&
            lastKeyboardState.IsKeyUp(key);
        }
        
        public static bool KeyDown(Keys key)
        {
            return keyboardState.IsKeyDown(key);
        }
        
        #endregion
        
        #region Game Pad Region
        
        public static bool ButtonReleased(Buttons button, PlayerIndex index)
        {
            return gamePadStates[(int)index].IsButtonUp(button) &&
            lastGamePadStates[(int)index].IsButtonDown(button);
        }
        
        public static bool ButtonPressed(Buttons button, PlayerIndex index)
        {
            return gamePadStates[(int)index].IsButtonDown(button) &&
            lastGamePadStates[(int)index].IsButtonUp(button);
        }
        
        public static bool ButtonDown(Buttons button, PlayerIndex index)
        {
            return gamePadStates[(int)index].IsButtonDown(button);
        }
        
        #endregion
    }
}